Master modern API development: RESTful design patterns, GraphQL implementation, authentication strategies, testing methodologies, documentation, and performance optimization techniques.
Foundational concepts for creating maintainable, scalable, and developer-friendly APIs.
Start with API specification before implementation to ensure consistency and enable parallel development.
Model APIs around resources (nouns) rather than actions (verbs) for intuitive and predictable interfaces.
Establish and follow conventions across all API endpoints for predictable developer experience.
Plan for API evolution without breaking existing clients through thoughtful versioning approaches.
Provide clear, actionable error messages with appropriate HTTP status codes and consistent structure.
Design APIs with performance in mind: efficient data transfer, caching, and scalability patterns.
Deep dive into REST architecture principles and implementation best practices.
// User Management API
// Get all users (with pagination)
GET /api/v1/users?page=1&limit=20&sort=created_at&order=desc
Response: 200 OK
{
"data": [
{
"id": "123e4567-e89b-12d3-a456-426614174000",
"email": "user@example.com",
"name": "John Doe",
"created_at": "2023-10-01T12:00:00Z",
"updated_at": "2023-10-15T14:30:00Z"
}
],
"pagination": {
"current_page": 1,
"per_page": 20,
"total_pages": 5,
"total_count": 100
},
"links": {
"next": "/api/v1/users?page=2&limit=20",
"last": "/api/v1/users?page=5&limit=20"
}
}
// Get specific user
GET /api/v1/users/123e4567-e89b-12d3-a456-426614174000
Response: 200 OK
{
"data": {
"id": "123e4567-e89b-12d3-a456-426614174000",
"email": "user@example.com",
"name": "John Doe",
"profile": {
"bio": "Software Developer",
"avatar_url": "https://example.com/avatars/123.jpg"
},
"created_at": "2023-10-01T12:00:00Z",
"updated_at": "2023-10-15T14:30:00Z"
}
}
// Create new user
POST /api/v1/users
Content-Type: application/json
{
"email": "newuser@example.com",
"name": "Jane Smith",
"password": "securePassword123!"
}
Response: 201 Created
Location: /api/v1/users/456e7890-e89b-12d3-a456-426614174001
// Update user
PATCH /api/v1/users/123e4567-e89b-12d3-a456-426614174000
Content-Type: application/json
{
"name": "John Smith"
}
Response: 200 OK
// Delete user
DELETE /api/v1/users/123e4567-e89b-12d3-a456-426614174000
Response: 204 No Content
// Error response example
GET /api/v1/users/invalid-id
Response: 400 Bad Request
{
"error": {
"code": "INVALID_USER_ID",
"message": "User ID must be a valid UUID",
"details": {
"field": "id",
"provided": "invalid-id"
}
}
}
Hypermedia as the Engine of Application State - Include navigation links in responses.
Support multiple representation formats based on client preferences.
Implement HTTP caching for improved performance and reduced server load.
Modern API approach with flexible queries, strong typing, and efficient data fetching.
# GraphQL Schema Definition
type User {
id: ID!
email: String!
name: String!
posts: [Post!]!
createdAt: DateTime!
updatedAt: DateTime!
}
type Post {
id: ID!
title: String!
content: String!
author: User!
comments: [Comment!]!
publishedAt: DateTime
createdAt: DateTime!
updatedAt: DateTime!
}
type Comment {
id: ID!
content: String!
author: User!
post: Post!
createdAt: DateTime!
}
type Query {
user(id: ID!): User
users(first: Int, after: String): UserConnection
post(id: ID!): Post
posts(
first: Int
after: String
filter: PostFilter
orderBy: PostOrderBy
): PostConnection
}
type Mutation {
createUser(input: CreateUserInput!): CreateUserPayload!
updateUser(id: ID!, input: UpdateUserInput!): UpdateUserPayload!
deleteUser(id: ID!): DeleteUserPayload!
createPost(input: CreatePostInput!): CreatePostPayload!
publishPost(id: ID!): PublishPostPayload!
addComment(input: AddCommentInput!): AddCommentPayload!
}
type Subscription {
postAdded(authorId: ID): Post
commentAdded(postId: ID!): Comment
}
input CreateUserInput {
email: String!
name: String!
password: String!
}
input PostFilter {
authorId: ID
published: Boolean
search: String
}
enum PostOrderBy {
CREATED_AT_ASC
CREATED_AT_DESC
TITLE_ASC
TITLE_DESC
}
# Pagination types
type UserConnection {
edges: [UserEdge!]!
pageInfo: PageInfo!
totalCount: Int!
}
type UserEdge {
node: User!
cursor: String!
}
type PageInfo {
hasNextPage: Boolean!
hasPreviousPage: Boolean!
startCursor: String
endCursor: String
}
# Fetch user with posts and comments
query GetUserWithPosts($userId: ID!) {
user(id: $userId) {
id
name
email
posts(first: 10) {
edges {
node {
id
title
publishedAt
comments(first: 5) {
edges {
node {
id
content
author {
name
}
createdAt
}
}
}
}
}
}
}
}
# Create new post mutation
mutation CreatePost($input: CreatePostInput!) {
createPost(input: $input) {
post {
id
title
content
author {
name
}
createdAt
}
errors {
field
message
}
}
}
# Subscribe to new comments
subscription CommentAdded($postId: ID!) {
commentAdded(postId: $postId) {
id
content
author {
name
}
createdAt
}
}
Secure API access with modern authentication patterns and authorization strategies.
# JWT Token Structure
# Header.Payload.Signature
# Header
{
"alg": "RS256",
"typ": "JWT",
"kid": "key-id-1"
}
# Payload (Claims)
{
"sub": "user-123", # Subject (user ID)
"iss": "https://api.example.com", # Issuer
"aud": "mobile-app", # Audience
"exp": 1698765432, # Expiration timestamp
"iat": 1698661432, # Issued at timestamp
"jti": "token-uuid", # JWT ID (for revocation)
"scope": "read write", # Permissions
"roles": ["user", "premium"], # User roles
"email": "user@example.com" # Custom claims
}
# Authentication Flow
POST /api/v1/auth/login
{
"email": "user@example.com",
"password": "securePassword123!"
}
Response: 200 OK
{
"access_token": "eyJhbGciOiJSUzI1NiIs...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "refresh_token_value",
"scope": "read write"
}
# Using JWT in requests
Authorization: Bearer eyJhbGciOiJSUzI1NiIs...
# Token refresh
POST /api/v1/auth/refresh
{
"refresh_token": "refresh_token_value"
}
Response: 200 OK
{
"access_token": "new_access_token",
"expires_in": 3600
}
Most secure flow for web applications with server-side component.
Server-to-server authentication without user involvement.
Authentication for devices without web browsers or limited input.
Comprehensive testing approaches for reliable and robust API implementations.
// Unit Test Example (Jest)
describe('User API', () => {
describe('POST /users', () => {
it('should create user with valid data', async () => {
const userData = {
email: 'test@example.com',
name: 'Test User',
password: 'SecurePass123!'
};
const response = await request(app)
.post('/api/v1/users')
.send(userData)
.expect(201);
expect(response.body.data).toMatchObject({
email: userData.email,
name: userData.name
});
expect(response.body.data.id).toBeDefined();
expect(response.body.data.password).toBeUndefined();
});
it('should return 400 for invalid email', async () => {
const userData = {
email: 'invalid-email',
name: 'Test User',
password: 'SecurePass123!'
};
const response = await request(app)
.post('/api/v1/users')
.send(userData)
.expect(400);
expect(response.body.error.code).toBe('INVALID_EMAIL');
});
});
});
// Integration Test Example
describe('User Integration Tests', () => {
beforeEach(async () => {
await clearDatabase();
await seedTestData();
});
it('should handle complete user lifecycle', async () => {
// Create user
const createResponse = await request(app)
.post('/api/v1/users')
.send(testUserData)
.expect(201);
const userId = createResponse.body.data.id;
// Get user
await request(app)
.get(`/api/v1/users/${userId}`)
.expect(200);
// Update user
await request(app)
.patch(`/api/v1/users/${userId}`)
.send({ name: 'Updated Name' })
.expect(200);
// Delete user
await request(app)
.delete(`/api/v1/users/${userId}`)
.expect(204);
// Verify deletion
await request(app)
.get(`/api/v1/users/${userId}`)
.expect(404);
});
});
# Performance Test Example (Artillery)
config:
target: 'https://api.example.com'
phases:
- duration: 60
arrivalRate: 10
- duration: 120
arrivalRate: 50
- duration: 60
arrivalRate: 100
scenarios:
- name: 'User API Load Test'
requests:
- get:
url: '/api/v1/users'
headers:
Authorization: 'Bearer {{ $randomString() }}'
- post:
url: '/api/v1/users'
json:
email: '{{ $randomString() }}@example.com'
name: '{{ $randomString() }}'
Create comprehensive documentation that enables developer success and API adoption.
openapi: 3.0.3
info:
title: User Management API
description: A comprehensive API for user management operations
version: 1.0.0
contact:
name: API Support
email: api-support@example.com
url: https://example.com/support
license:
name: MIT
url: https://opensource.org/licenses/MIT
servers:
- url: https://api.example.com/v1
description: Production server
- url: https://staging-api.example.com/v1
description: Staging server
security:
- bearerAuth: []
- apiKey: []
paths:
/users:
get:
summary: List users
description: Retrieve a paginated list of users with optional filtering
operationId: getUsers
tags:
- Users
parameters:
- name: page
in: query
description: Page number for pagination
required: false
schema:
type: integer
minimum: 1
default: 1
- name: limit
in: query
description: Number of users per page
required: false
schema:
type: integer
minimum: 1
maximum: 100
default: 20
- name: search
in: query
description: Search term for filtering users
required: false
schema:
type: string
maxLength: 100
responses:
'200':
description: Users retrieved successfully
content:
application/json:
schema:
$ref: '#/components/schemas/UsersResponse'
examples:
success:
summary: Successful response
value:
data:
- id: "123e4567-e89b-12d3-a456-426614174000"
email: "user@example.com"
name: "John Doe"
created_at: "2023-10-01T12:00:00Z"
pagination:
current_page: 1
per_page: 20
total_pages: 5
total_count: 100
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'500':
$ref: '#/components/responses/InternalError'
post:
summary: Create user
description: Create a new user account
operationId: createUser
tags:
- Users
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateUserRequest'
examples:
valid_user:
summary: Valid user creation
value:
email: "newuser@example.com"
name: "Jane Smith"
password: "SecurePass123!"
responses:
'201':
description: User created successfully
headers:
Location:
description: URL of the created user
schema:
type: string
content:
application/json:
schema:
$ref: '#/components/schemas/UserResponse'
'400':
$ref: '#/components/responses/BadRequest'
'409':
description: User already exists
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
components:
schemas:
User:
type: object
required:
- id
- email
- name
- created_at
- updated_at
properties:
id:
type: string
format: uuid
description: Unique user identifier
example: "123e4567-e89b-12d3-a456-426614174000"
email:
type: string
format: email
description: User's email address
example: "user@example.com"
name:
type: string
minLength: 1
maxLength: 100
description: User's full name
example: "John Doe"
created_at:
type: string
format: date-time
description: User creation timestamp
example: "2023-10-01T12:00:00Z"
updated_at:
type: string
format: date-time
description: User last update timestamp
example: "2023-10-15T14:30:00Z"
CreateUserRequest:
type: object
required:
- email
- name
- password
properties:
email:
type: string
format: email
description: User's email address
example: "user@example.com"
name:
type: string
minLength: 1
maxLength: 100
description: User's full name
example: "John Doe"
password:
type: string
minLength: 8
maxLength: 128
description: User's password (minimum 8 characters)
example: "SecurePass123!"
responses:
BadRequest:
description: Bad request - Invalid input data
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
Unauthorized:
description: Unauthorized - Invalid or missing authentication
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
apiKey:
type: apiKey
in: header
name: X-API-Key
Practice API development concepts with hands-on tools and simulators.
Comprehensive assessment of API development skills and best practices.
Choose GraphQL when you need flexible data fetching, have multiple client types with different data needs, want to reduce over-fetching, need real-time subscriptions, or work with complex, interconnected data. REST is better for simpler CRUD operations, caching requirements, or when you need mature tooling ecosystem.
Use semantic versioning, maintain backward compatibility for minor versions, provide clear deprecation timelines, support multiple API versions simultaneously, use content negotiation for version selection, and implement gradual migration strategies with proper documentation.
Consider business requirements, user tiers, endpoint sensitivity, burst vs sustained load, fair usage policies, rate limit algorithms (token bucket, sliding window), proper error responses (429 status), and rate limit headers for client guidance.
Implement proper authentication/authorization, validate all inputs, use HTTPS everywhere, protect against injection attacks, implement rate limiting, use CORS correctly, validate JWT tokens properly, sanitize outputs, and regularly update dependencies.
Implement caching (HTTP, Redis, CDN), optimize database queries, use pagination and filtering, implement compression, use async processing for heavy operations, implement circuit breakers, monitor performance metrics, and scale horizontally with load balancing.